import logging
import time
import os
import sys
from config import config_singleton, config_model_singleton
import cProfile
import pstats
from logging.handlers import RotatingFileHandler
import abc

from pstats import SortKey


class _Constants:
    """
    用于定义系统的常量
    """
    class ConstError(TypeError):
        pass

    def __setattr__(self, name: str, value) -> None:
        if name in self.__dict__:
            raise self.ConstError("Can't rebind const (%s)" % name)
        self.__dict__[name] = value


# 所以得常量定义都放在下面
Constants = _Constants()
Constants.UDP_FRAME_VERSION = config_singleton.udp_frame_heads["version"]
Constants.UDP_FRAME_TASKID = config_singleton.udp_frame_heads["taskid"]
Constants.UDP_FRAME_SID = config_singleton.udp_frame_heads["sid"]
Constants.UDP_FRAME_DID = config_singleton.udp_frame_heads["did"]
Constants.UDP_FRAME_BID = config_singleton.udp_frame_heads["bid"]
Constants.UDP_FRAME_HEAD_LEN = config_singleton.udp_frame_heads["head_len"]

Constants.UDP_FRAME_HEADER = (Constants.UDP_FRAME_VERSION, Constants.UDP_FRAME_TASKID,
                              Constants.UDP_FRAME_SID, Constants.UDP_FRAME_DID, Constants.UDP_FRAME_BID)


# TCP相关常量
Constants.TCP_FRAME_HEAD = config_singleton.tcp_frame_settings["head"]
Constants.TCP_FRAME_ID = config_singleton.tcp_frame_settings["id"]
Constants.TCP_FRAME_TAIL = config_singleton.tcp_frame_settings["tail"]
Constants.TCP_FRAME_HEAD_LEN = config_singleton.tcp_frame_settings["head_len"]

# 流程数据解码相关常量
Constants.FLOW_FRAME_HEAD = config_singleton.flow_frame_settings["head"]
Constants.FLOW_FRAME_ID = config_singleton.flow_frame_settings["id"]
Constants.FLOW_FRAME_TAIL = config_singleton.flow_frame_settings["tail"]
Constants.FLOW_FRAME_HEAD_LEN = config_singleton.flow_frame_settings["head_len"]
Constants.FLOW_FRAME_NUM = config_singleton.flow_frame_settings["table_num"]
Constants.FLOW_FRAME_LEN_POS = config_singleton.flow_frame_settings["frame_len_position"]

class BaseBean:
    """
    作为MsgBean和NetMsgBean的基类
    """
    def __init__(self) -> None:
        self.table_num = 0

class MsgBean(BaseBean):
    """
    消息bean，用于封装消息数据，UDP接收的数据，一个Bean表代一帧数据
    """

    def __init__(self) -> None:
        BaseBean.__init__(self)
        self.var_num = 0
        self.bus_gap = 0
        # 第一个元素默认为0，无效值，不使用。另外一个解决方案是使用numpy, 可以使用切片的方式获取数据
        self.vars = [0.0]

class FlowCode:
    """
    网络编码，包含网络编码，实测值，偏差值和和个标记
    """
    def __init__(self) -> None:
        self.code = 0
        self.measured_value = 0.0
        self.relative_value = 0.0
        self.flag = False

class NetMsgBean(BaseBean):
    """
    流程消息相关内容，完整一帧流程内容
    """
    def __init__(self) -> None:
        BaseBean.__init__(self)
        self.function_no = 0
        self.sub_function_no = 0
        self.code_num = 0
        self.destination_id = 0
        self.codes = []     # FlowCode list


class BaseClient(metaclass=abc.ABCMeta):
    """
    tcp udp client base class for TCP and UDP clients
    """
    @abc.abstractmethod
    def __init__(self, ip, port, queue_size):
        pass

    @abc.abstractmethod
    def do_receive(self):
        pass

    @abc.abstractmethod
    def do_send(self):
        pass

    @abc.abstractmethod
    def get_recv_data(self):
        pass

    @abc.abstractmethod
    def put_send_data(self, data):
        pass


    @abc.abstractmethod
    def stop(self):
        pass




def setup_logger():
    """
    默认系统日志，同时输出至stdout,file
    可以根据配置文件对输出进行重定向。
    """
    logger = logging.getLogger("ADSys")
    logger.setLevel(logging.INFO)
    # stdout = logging.StreamHandler(sys.stdout)
    # stdout.setLevel(logging.INFO)
    # logger.addHandler(stdout)

    # StreamHandler
    if config_singleton.log_setting["log_to_screen"]:
        stream_handler = logging.StreamHandler(sys.stdout)
        stream_handler.setLevel(level=logging.INFO)
        formatter = logging.Formatter(
            datefmt='%Y/%m/%d %H:%M:%S', fmt='[ %(asctime)s ] %(message)s')
        stream_handler.setFormatter(formatter)
        logger.addHandler(stream_handler)

    # FileHandler
    if config_singleton.log_setting["log_to_file"]:
        log_file_path = os.path.join(config_singleton.log_setting["log_file_path"], time.strftime(
            "%Y%m%d_%H%M%S", time.localtime()))
        os.makedirs(log_file_path)
        if config_singleton.write_to_tensorboard:
            config_singleton.tensorboard_log_root = log_file_path         # 设置全局的log file path
        file_handler = RotatingFileHandler(os.path.join(
            log_file_path, "out.log"), maxBytes=1024000, backupCount=5)
        file_handler.setLevel(level=logging.INFO)
        formatter = logging.Formatter(
            '%(asctime)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

        # 把config信息也记录到log 文件中
        config_dict = {}
        for key in dir(config_singleton):
            if not key.startswith("_"):
                config_dict[key] = getattr(config_singleton, key)

        for key in dir(config_model_singleton):
            if not key.startswith("_"):
                config_dict[key] = getattr(config_model_singleton, key)

        config_lines = [""]
        for item in config_dict.keys():
            if item == "model_configs":
                config_lines = config_lines + [item + ": "]
                for model_conf in config_dict[item]:
                    config_lines = config_lines + ["\t" + str(model_conf)]
            else:
                temp = item + ": \n\t" + str(config_dict[item])
                config_lines = config_lines + [temp]

        # config_save_str = "\n\nCONFIGS:" + "\n".join(config_lines) + "\n\n"
        # logger.info(config_save_str)

    return logger


sys_logger = setup_logger()


def app_profile_test(function_name):
    """
    使用cProfile对函数进行性能分析
    """
    cProfile.run(function_name, "profile.prof")

    p = pstats.Stats("profile.prof")
    # 打印所有统计信息
    p.strip_dirs().sort_stats("tottime").print_stats()
    # 打印总耗时最高的的10个函数
    p.sort_stats(SortKey.CUMULATIVE).print_stats(10)
    # 打印内部耗时最高的10个函数
    p.sort_stats(SortKey.TIME).print_stats(10)


if __name__ == "__main__":
    # 测试
    bean = NetMsgBean()
    app_profile_test("setup_logger()")

